﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Data;

namespace OpenFrameworkCore.Controls
{
    /// <summary>
    /// This class, ControlContainerBase, hosts control inherted from OpenFrameworkCore.Controls.ControlBase.
    /// <seealso cref="OpenFrameworkCore.Controls.ControlBase"/>
    /// </summary>
	[TemplatePart(Name = ROOT_ELEMENT_NAME, Type = typeof(Panel))]
	[TemplatePart(Name = CONTROL_HOST_NAME, Type = typeof(Canvas))]
	public abstract class ControlContainerBase : System.Windows.Controls.Control
    {
		private const string ROOT_ELEMENT_NAME = "RootElement";
		private const string CONTROL_HOST_NAME = "ControlHost";

		/// <summary>
		/// 템플릿이 로드되었는지 정보
		/// </summary>
		private bool isTemplateLoaded;

		/// <summary>
		/// RootElement
		/// </summary>
		protected Panel RootElement;

		/// <summary>
		/// Control들이 호스팅되는 컨트롤
		/// </summary>
		protected Canvas ControlHost;

        /// <summary>
        /// ControlContextToControlMapper is mapping BasicControlContext and ControlBase.
        /// </summary>
        protected IDictionary<ControlBaseContext, ControlBase> ControlContextToControlMapper;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <remarks>
        /// Constructor is initializing member varialbes.
        /// </remarks>
        public ControlContainerBase()
        {
			ControlContextToControlMapper = new Dictionary<ControlBaseContext, ControlBase>();
        }

        public static readonly DependencyProperty ControlContainerContextProperty = DependencyProperty.Register
            (
            "ControlContainerContext",
            typeof(BasicControlContainerContext),
            typeof(ControlContainerBase),
            new PropertyMetadata(null, ControlContainerContextPropertyChanged)
            );

        private static void ControlContainerContextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ControlContainerBase)d).ControlContainerContextPropertyChanged(e);
        }

        private void ControlContainerContextPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            BasicControlContainerContext oldValue = (BasicControlContainerContext)e.OldValue;
            BasicControlContainerContext newValue = (BasicControlContainerContext)e.NewValue;

            // Binding 제거
            if (oldValue != null)
                ClearContextToElementBinding();

            // Binding 추가
            if (newValue != null)
                InitContextToElementBinding();
        }

        /// <summary>
        /// BasicControlContainerContext
        /// </summary>
        public BasicControlContainerContext ControlContainerContext
        {
            get
            {
                return (BasicControlContainerContext)GetValue(ControlContainerContextProperty);
            }
            set
            {
                SetValue(ControlContainerContextProperty, value);
            }
        }

        public static readonly DependencyProperty ControlContextSourceProperty = DependencyProperty.Register
            (
            "ControlContextsSource",
            typeof(ObservableCollection<BasicControlContext>),
            typeof(ControlContainerBase),
            new PropertyMetadata(null, OnControlContextSourcePropertyChanged)
            );

        private static void OnControlContextSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ControlContainerBase)d).OnControlContextSourcePropertyChanged(e);
        }

        private void OnControlContextSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            ObservableCollection<BasicControlContext> oldControlBaseContexts = (ObservableCollection<BasicControlContext>)e.OldValue;
            ObservableCollection<BasicControlContext> newControlBaseContexts = (ObservableCollection<BasicControlContext>)e.NewValue;

            if (oldControlBaseContexts != null)
            {
                // 기존 Collection에 존재하던 아이템 삭제
                foreach (BasicControlContext controlBaseContext in oldControlBaseContexts)
                    OnControlContextRemoved(controlBaseContext);

                // Event handler detach.
                oldControlBaseContexts.CollectionChanged -=
                    new System.Collections.Specialized.NotifyCollectionChangedEventHandler(OnControlContextsCollectionChanged);
            }

            if (newControlBaseContexts != null)
            {
                // 기존 Collection에 존재하던 아이템 추가
				foreach (BasicControlContext controlBaseContext in newControlBaseContexts)
					OnControlContextAdded(controlBaseContext);

                // If BasicControlContainerContext's property is changed,
                // OnControlConetxtSourcePropertyChanged method is called for processing.
                newControlBaseContexts.CollectionChanged +=
                    new System.Collections.Specialized.NotifyCollectionChangedEventHandler(OnControlContextsCollectionChanged);
            }
        }

        private void OnControlContextsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // ControlContainer의 ControlContext가 변경됐을때 호출되는 메서드
            switch (e.Action)
            {   
                case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
                    // 추가된 아이템을 하나씩 추출해 OnControlContextAdded 메서드의 인자로 준다.
					foreach (BasicControlContext controlContext in e.NewItems)
						// OnControlContextAdded 메서드 호출
						// ControlContainerBase를 상속받은 클래스에서 구현한 소스를 호출하기 위함
						OnControlContextAdded(controlContext);

                    break;

                case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
                    // 삭제된 아이템을 하나씩 추출해 OnControlContextRemoved 메서드의 인자로 준다.
					foreach (BasicControlContext controlContext in e.OldItems)
						// OnControlContextRemoved 메서드 호출
						OnControlContextRemoved(controlContext);

                    break;

                case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
                    // 호출되지 않는 것 같다.
                    throw new NotImplementedException();

                case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
                    // ContextSource의 Clear() 메서드를 호출했을때 발생하는 action
                    // 모든 아이템을 삭제해준다.
					foreach (BasicControlContext controlContext in ControlContextToControlMapper.Keys.ToList())
						// OnControlContextRemoved 메서드 호출
						OnControlContextRemoved(controlContext);

                    break;
            }
        }

        /// <summary>
        /// ControlContext의 Source collection
        /// </summary>
        public System.Collections.ObjectModel.ObservableCollection<BasicControlContext> ControlContextsSource
        {
            get
            {
                return (ObservableCollection<BasicControlContext>)GetValue(ControlContextSourceProperty);
            }

            set
            {
                SetValue(ControlContextSourceProperty, value);
            }
        }

		/// <summary>
		/// 현재 등록된 ControlContext 리스트
		/// </summary>
		public IList<ControlBaseContext> ControlContexts
		{
			get
			{
				return ControlContextToControlMapper.Keys.ToList();
			}
		}

		/// <summary>
		/// 현재 등록된 Control 리스트
		/// </summary>
		public IList<ControlBase> Controls
		{
			get
			{
				return ControlContextToControlMapper.Values.ToList();
			}
		}

		/// <summary>
		/// 디자인 템플릿이 적용된 경우 호출되는 메서드
		/// </summary>
		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			// Search element by name
			RootElement = (Panel)GetTemplateChild(ROOT_ELEMENT_NAME);
			if (RootElement == null)
				throw new TemplatePartNotFoundException("RootElement를 찾을 수 없습니다.");

			ControlHost = (Canvas)GetTemplateChild(CONTROL_HOST_NAME);
			if (ControlHost == null)
				throw new TemplatePartNotFoundException("ControlHost를 찾을 수 없습니다.");

			// 기존에 있던 ControlContext들을 화면에 출력한다.
			foreach (ControlBase control in ControlContextToControlMapper.Values)
			{
				ControlHost.Children.Add(control);

				// OnControlAdded 메서드 호출
				OnControlAdded(control);
			}

			isTemplateLoaded = true;
		}

        /// <summary>
        /// ControlContext가 추가된 경우 호출되는 메서드
        /// </summary>
        protected virtual void OnControlContextAdded(BasicControlContext addedControlContext)
        {
			// ControlContext를 기반으로 Control 생성
			ControlBase newControl = ContextToControl(addedControlContext);

			// OwnerControlContainer를 현재 ControlContainer로 설정
			newControl.OwnerControlContainer = this;

			// Context와 Control mapping
			ControlContextToControlMapper[addedControlContext] = newControl;

			// Canvas에 Control 출력
			if (isTemplateLoaded)
			{
				// 화면에 추가
				ControlHost.Children.Add(newControl);

				// OnControlAdded 메서드 호출
				OnControlAdded(newControl);
			}
        }

        /// <summary>
        /// ControlContext가 제거된 겨우 호출되는 메서드
        /// </summary>
        protected virtual void OnControlContextRemoved(BasicControlContext removedControlContext)
		{
			// 삭제할 Control을 찾는다.
			ControlBase removeControl = ControlContextToControlMapper[removedControlContext];

			// Mapper에서 삭제
			ControlContextToControlMapper.Remove(removedControlContext);

			// Canvas에 Control 출력
			if (isTemplateLoaded)
			{
				// 화면에서 삭제
				ControlHost.Children.Remove(removeControl);

				// OnControlRemoved 메서드 호출
				OnControlRemoved(removeControl);
			}
        }

        /// <summary>
        /// Control이 추가된 경우 호출되는 메서드
        /// </summary>
        protected virtual void OnControlAdded(ControlBase targetControl)
        {
        }

        /// <summary>
        /// Control이 제거된 경우 호출되는 메서드
        /// </summary>
        protected virtual void OnControlRemoved(ControlBase removedControl)
        {
        }

        /// <summary>
        /// Binding을 초기화합니다.
        /// </summary>
        protected virtual void InitContextToElementBinding()
        {
            // ControlContexts를 바인딩합니다.
            Binding controlContextsBinding = new Binding("ControlContexts");
            controlContextsBinding.Source = ControlContainerContext;
            controlContextsBinding.Mode = BindingMode.TwoWay;
            SetBinding(ControlContextSourceProperty, controlContextsBinding);
        }

        /// <summary>
        /// Binding을 제거합니다.
        /// </summary>
		protected virtual void ClearContextToElementBinding()
        {
            ClearValue(ControlContextSourceProperty);
        }

        /// <summary>
        /// ControlContext를 기반으로 Control을 생성하는 메서드
        /// </summary>
        protected ControlBase ContextToControl(BasicControlContext controlBaseContext)
        {
            // 생성할 Control
            ControlBase control = (ControlBase)Activator.CreateInstance(Type.GetType(controlBaseContext.ControlClass));

            // ControlContext 설정
            control.ControlContext = controlBaseContext;

            return control;
        }
    }
}
